package orma

import (
	"errors"
	"fmt"
	"path/filepath"
	"time"

	"github.com/go-xorm/core"
	"github.com/go-xorm/xorm"

	"gitee.com/goframe/lt-collector/libs/abio"
)

type (
	DB struct {
		Type        string `json:"type" ini:"type"`
		Host        string `json:"host" ini:"host"`
		Port        int    `json:"port" ini:"port"`
		User        string `json:"user" ini:"user"`
		Password    string `json:"password" ini:"password"`
		DB          string `json:"db" ini:"db"`
		Cache       bool   `json:"cache" ini:"cache"`
		ShowSQL     bool   `json:"show_sql" ini:"show_sql"`
		MaxIdleConn int    `json:"max_idle_conn" ini:"max_idle_conn" comment:"数据库连接最大空闲数"`
		MaxConn     int    `json:"max_conn" ini:"max_conn" comment:"最大连接数"`
	}
)

func (db DB) LoadEngine() (err error) {
	dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?timeout=30s&charset=utf8mb4&parseTime=true",
		db.User,
		db.Password,
		db.Host,
		db.Port,
		db.DB,
	)
	engine, err = xorm.NewEngine(db.Type, dsn)
	if err != nil {
		return
	}
	engine.SetMaxIdleConns(db.MaxIdleConn)
	engine.SetMaxOpenConns(db.MaxConn)
	engine.SetConnMaxLifetime(0)
	engine.SetMapper(core.GonicMapper{})
	engine.ShowSQL(db.ShowSQL)
	if db.ShowSQL {
		fName := filepath.Join(abio.SelfDir(), "logs", "sql", "sql.log")
		engine.SetLogger(xorm.NewSimpleLogger(abio.NewAbFile(fName, nil)))
		engine.SetLogLevel(core.LOG_INFO)
		engine.ShowExecTime()
	} else {
		engine.SetLogLevel(core.LOG_OFF)
	}
	engine.SetDisableGlobalCache(!db.Cache)
	if db.Cache {
		cacher := xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)
		engine.SetDefaultCacher(cacher)
	}
	ping := func() {
		tk := time.NewTicker(30 * time.Second)
		for {
			select {
			case <-tk.C:
				_ = engine.Ping()
			}
		}
	}
	go ping()
	return
}

func (db DB) CreateDB() (err error) {
	dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/?timeout=30s&charset=utf8mb4&parseTime=true",
		db.User,
		db.Password,
		db.Host,
		db.Port,
	)
	engine, err = xorm.NewEngine(db.Type, dsn)
	if err != nil {
		return
	}
	createDBSql := fmt.Sprintf(
		"CREATE DATABASE IF NOT EXISTS %s default character set utf8mb4 COLLATE utf8mb4_unicode_ci;",
		db.DB,
	)
	if _, err = engine.Exec(createDBSql); err != nil {
		return
	}
	return
}

var (
	ErrOrmNotFound    = errors.New("record not found")
	ErrOrmNotModified = errors.New("record not modified")
	ErrOrmZeroResult  = errors.New("record zero result")
)

func SnCallBack(fn func(bsn *xorm.Session) error) (err error) {
	sn := engine.NewSession()
	if err = sn.Begin(); err != nil {
		return
	}
	defer sn.Close()
	if err = fn(sn); err != nil {
		_ = sn.Rollback()
		return
	}
	return sn.Commit()
}

func NormalGet(has bool, err error) error {
	if err != nil {
		return err
	}
	if !has {
		return ErrOrmNotFound
	}
	return nil
}

func NormalUpdate(a int64, err error) error {
	if err != nil {
		return err
	}
	if a == 0 {
		return ErrOrmNotModified
	}
	return nil
}

func NormalInsert(a int64, err error) error {
	if err != nil {
		return err
	}
	if a == 0 {
		return ErrOrmZeroResult
	}
	return nil
}

func NormalDelete(a int64, err error) error {
	if err != nil {
		return err
	}
	if a == 0 {
		return ErrOrmZeroResult
	}
	return nil
}

func ExecuteSQL(tx *xorm.Session, sqls []string) error {
	for _, sq := range sqls {
		if _, err := tx.Exec(sq); err != nil {
			return err
		}
	}
	return nil
}
